#include <QApplication>
#include <QKeyEvent>
#include <QMessageBox>
#include "matwin.h"
#include "matitem.h"

class MatMaze : public MatWin {
public:
    enum Item {
        kItemEmpty = MatItem::kTypeEmpty,
        kItemWall =  MatItem::kTypeFill,
        kItemPlayer = MatItem::kTypeCustom + 1,
        kItemEntrance = MatItem::kTypeCustom + 2,
        kItemExit = MatItem::kTypeCustom + 3,
    };

    MatMaze(qint32 m, qint32 n, qreal size, QWidget *parent = 0)
            : MatWin(m, n, size, parent), m_player(Q_NULLPTR) {
        setupUi();
        setFocus(); // for arrow keys
    }

    MatMaze(std::initializer_list<qint32> types, qint32 m, qint32 n, qreal size,
            QWidget *parent = 0)
            : MatMaze(m, n, size, parent) {
        size_t i = 0;
        qint32 e_row = -1, e_col = -1;
        for (const auto &t : types) {
            itemAt(i/n, i%n)->setItemType(t);
            if (t == kItemEntrance) {
                e_row = i/n;
                e_col = i%n;
            }
            ++i;
        }

        if (e_row != -1) {
            // set player on entrance
            setPlayer(e_row, e_col);
        }
    }

    virtual ~MatMaze() {}

    void setPlayer(qint32 m, qint32 n) {
        MatItem *next = itemAt(m, n);
        if (next->itemType() == kItemWall) {
            // not walkable on wall
            return;
        }
        if (m_player) {
            m_player->setItemType(m_itemType);
        }
        m_player = next;
        m_itemType = m_player->itemType();
        m_player->setItemType(kItemPlayer);
        update();

        if (m_itemType == kItemExit) {
            onExit();
        }
    }

    virtual void onExit() {
        QMessageBox::information(this, "Congratulation", "Well Done!");
    }

protected:
    virtual void resizeEvent(QResizeEvent *event) {
        Q_UNUSED(event)
        setFocus(); // for arrow keys
    }

    virtual void keyPressEvent(QKeyEvent *event) {
        switch (event->key()) {
        case Qt::Key_Left:
        case Qt::Key_A:
            moveLeft();
            break;
        case Qt::Key_Up:
        case Qt::Key_W:
            moveUp();
            break;
        case Qt::Key_Right:
        case Qt::Key_D:
            moveRight();
            break;
        case Qt::Key_Down:
        case Qt::Key_S:
            moveDown();
            break;
        }
    }

    void moveLeft() {
        QPoint pos = m_player->position();
        if (pos.x() <= 0) return;
        setPlayer(pos.y(), pos.x()-1);
    }

    void moveUp() {
        QPoint pos = m_player->position();
        if (pos.y() <= 0) return;
        setPlayer(pos.y()-1, pos.x());
    }

    void moveRight() {
        QPoint pos = m_player->position();
        if (pos.x() >= n()-1) return;
        setPlayer(pos.y(), pos.x()+1);
    }

    void moveDown() {
        QPoint pos = m_player->position();
        if (pos.y() >= m()-1) return;
        setPlayer(pos.y()+1, pos.x());
    }

private:
    void setupUi() {
        //setItemsColor(kItemEmpty, Qt::white);
        setItemsColor(kItemWall, Qt::gray);
        setItemsColor(kItemPlayer, Qt::magenta);
        setItemsColor(kItemEntrance, Qt::yellow);
        setItemsColor(kItemExit, Qt::green);
    }

    MatItem *m_player;
    qint32 m_itemType;
};

int main(int argc, char *argv[]) {
    QApplication a(argc, argv);

    qint32 A = MatMaze::kItemEntrance;
    qint32 B = MatMaze::kItemExit;
    MatMaze w({
        A, 0, 0, 0, 0, 0, 0, 0, 0, 0,
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
        0, 0, 1, 1, 1, 1, 1, 1, 0, 0,
        0, 0, 0, 0, 0, 0, 0, 1, 0, 0,
        0, 0, 1, 0, 0, 0, 0, 1, 0, 0,
        0, 0, 1, 0, 0, 0, 0, 1, 0, 0,
        0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
        0, 0, 1, 1, 1, 1, 1, 1, 0, 0,
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
        0, 0, 0, 0, 0, 0, 0, 0, 0, B,
    }, 10, 10, 20);
    w.show();

    return a.exec();
}
